/*

* ------------------------------------------------------------------------
* ------------------------------------------------------------------------
* |     Smart-Shop开源商城系统/ Java商城-首选电商平台系统 请务必保留此注释头信息
* |     开源地址: https://gitee.com/ningbo-qishan/gruul-mall
* ------------------------------------------------------------------------
* ------------------------------------------------------------------------
* |     可正常分享和学习源码,未经授权 不可商用！
* |     商业版中台购买/开源版商业授权 联系技术客服
* |     官网:  https://www.73app.cn/
* ------------------------------------------------------------------------
* ------------------------------------------------------------------------
* |     Copyright (c) 2016-2999 宁波启山智软科技有限公司
* |     版权所有 ,侵权必究！
* ------------------------------------------------------------------------

*/
package com.medusa.gruul.platform.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.map.MapBuilder;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.medusa.gruul.common.core.constant.CommonConstants;
import com.medusa.gruul.common.core.exception.ServiceException;
import com.medusa.gruul.common.core.util.LocalDateTimeUtils;
import com.medusa.gruul.common.core.util.PageUtils;
import com.medusa.gruul.platform.api.entity.AccountInfo;
import com.medusa.gruul.platform.api.entity.AgentInfo;
import com.medusa.gruul.platform.api.entity.AgentRecharge;
import com.medusa.gruul.platform.api.entity.PlatformAccountRecharge;
import com.medusa.gruul.platform.conf.MeConstant;
import com.medusa.gruul.platform.constant.AgentNoticeEnum;
import com.medusa.gruul.platform.mapper.AgentRechargeMapper;
import com.medusa.gruul.platform.model.dto.OrderOptionDto;
import com.medusa.gruul.platform.model.dto.PayDto;
import com.medusa.gruul.platform.model.dto.agent.AgentBalanceRechargeDto;
import com.medusa.gruul.platform.model.vo.BalanceRechargeVo;
import com.medusa.gruul.platform.model.vo.SystemConfigVo;
import com.medusa.gruul.platform.model.vo.agent.BalanceDetailsVo;
import com.medusa.gruul.platform.service.*;
import com.medusa.gruul.platform.stp.StpAgentUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 * 代理商充值记录表 服务实现类
 * </p>
 *
 * @author whh
 * @since 2020-10-04
 */
@Service
@Slf4j
public class AgentRechargeServiceImpl extends ServiceImpl<AgentRechargeMapper, AgentRecharge> implements IAgentRechargeService {

    @Autowired
    private ISystemConfService systemConfService;
    @Autowired
    private IAccountInfoService accountInfoService;
    @Autowired
    private IPlatformAccountBalanceRecordService platformAccountBalanceRecordService;
    @Autowired
    private IAgentInfoService agentInfoService;
    @Autowired
    private IPlatformAccountRechargeService platformAccountRechargeService;
    @Autowired
    private IAgentNoticeService agentNoticeService;

    @Override
    public AgentRecharge newAgentRecharge(AccountInfo accountInfo, Long agentId, Integer payType,
                                          BigDecimal payAmount, String payInfo) {
        Snowflake snowflake = IdUtil.getSnowflake(1, 1);
        AgentRecharge agentRecharge = new AgentRecharge()
                .setAgentInfoId(agentId)
                .setMerchantId(accountInfo.getId())
                .setRechargeNum(snowflake.nextIdStr())
                .setPayType(payType)
                .setPayAmount(payAmount)
                .setPayInfo(payInfo)
                .setAccountAmount(accountInfo.getBalance().add(payAmount));
        //除汇款支付,其他支付方式都是支付回调成功之后直接完成,才会显示订单
        agentRecharge.setStatus(CommonConstants.NUMBER_ZERO);
        int insert = this.getBaseMapper().insert(agentRecharge);
        if (insert == 0) {
            throw new ServiceException("新增失败数据");
        }
        return agentRecharge;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public BalanceRechargeVo agentBalanceRecharge(AgentBalanceRechargeDto dto) {
        if (CommonConstants.NUMBER_THREE.equals(dto.getPayType())) {
            if (StrUtil.isEmpty(dto.getPayInfo())) {
                throw new ServiceException("付款方信息,请勿为空!");
            }
        }
        //获取代理信息
        AgentInfo agentInfo = agentInfoService.getById(StpAgentUtil.getLoginIdAsLong());

        AccountInfo accountInfo = accountInfoService.getById(dto.getAccountId());
        if (ObjectUtil.isNull(accountInfo)) {
            throw new ServiceException("用户数据错误");
        }
        SystemConfigVo systemConfigVo = systemConfService.getTypeInfo(CommonConstants.NUMBER_ZERO);
        if (ObjectUtil.isEmpty(systemConfigVo)) {
            throw new ServiceException("支付渠道错误,请联系平台!");
        }
        String notifyUrl = systemConfigVo.getSystemConfig().getMiniDomain().concat("/api/platform/agent/console/balance/notify");
        AgentRecharge agentRecharge = this.newAgentRecharge(accountInfo, agentInfo.getId(), dto.getPayType(), dto.getBalance(), dto.getPayInfo());
        //支付方式转换为商户订单对应支付类型
        Integer payType =  NumberUtil.add(CommonConstants.NUMBER_TEN, dto.getPayType()).intValue();
        platformAccountRechargeService.newPlatformAccountRecharge(
                accountInfo, agentInfo,
                payType, dto.getBalance(),
                dto.getPayInfo(), agentRecharge.getRechargeNum());
        try {
            BalanceRechargeVo vo = new BalanceRechargeVo();
            vo.setOrderId(agentRecharge.getId());
            AgentRecharge up = new AgentRecharge();
            switch (dto.getPayType()) {
                case 1:
                    PayDto payDto1 = systemConfService.aliPayQrcodePay(notifyUrl.concat("/alipay"), "余额充值", agentRecharge.getPayAmount().toString(), agentRecharge.getRechargeNum());
                    vo.setCodeUrl(payDto1.getCodeUrl());
                    break;
                case 2:
                    PayDto payDto = systemConfService.wxQrcodePay(notifyUrl.concat("/wx"), agentRecharge.getRechargeNum(), agentRecharge.getPayAmount().toString(), "用户余额充值", "agentBlanceRecharge");
                    up.setPrepayId(payDto.getPrepayId());
                    vo.setCodeUrl(payDto.getCodeUrl());
                    break;
                case 3:
                    up.setPayInfo(agentRecharge.getPayInfo());
                    up.setStatus(CommonConstants.NUMBER_ONE);
                    break;
                case 4:
                    //余额支付支付成功直接返回
                    agentBlanceBuy(agentRecharge);
                    break;
                default:
                    throw new ServiceException("业务异常");
            }
            //更新数据
            this.baseMapper.update(up, new QueryWrapper<AgentRecharge>().eq("id", agentRecharge.getId()));
            return vo;
        } catch (ServiceException serviceException) {
            serviceException.printStackTrace();
            throw new ServiceException(serviceException.getMsg());
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceException("支付异常,请联系平台!");
        }
    }

    /**
     * 代理余额充值
     *
     * @param agentRecharge 充值订单
     */
    private void agentBlanceBuy(AgentRecharge agentRecharge) {
        AgentInfo agentInfo = agentInfoService.getById(StpAgentUtil.getLoginIdAsLong());
        BigDecimal newBalance = NumberUtil.sub(agentInfo.getAvailableAmount(), agentRecharge.getPayAmount());
        if (newBalance.compareTo(new BigDecimal(MeConstant.ZERO)) < 0) {
            throw new ServiceException("账户余额不足");
        }
        AgentInfo up = new AgentInfo();
        up.setId(agentInfo.getId());
        up.setAvailableAmount(newBalance);
        agentInfoService.updateById(up);
        payNotify(agentRecharge.getRechargeNum());
    }

    @Override
    public Boolean agentBalanceRechargePayIfOk(Long orderId) {
        AgentRecharge agentRecharge = this.getById(orderId);
        if (agentRecharge == null) {
            throw new ServiceException("不存在该订单");
        }
        if (agentRecharge.getStatus().equals(CommonConstants.NUMBER_TWO)) {
            return Boolean.TRUE;
        }
        return Boolean.FALSE;
    }

    @Override
    public String rechargeNotifyWx(String xmlResult) {
        WxPayOrderNotifyResult payOrderNotifyResult = WxPayOrderNotifyResult.fromXML(xmlResult);
        String outTradeNo = payOrderNotifyResult.getOutTradeNo();
        if (payNotify(outTradeNo)) {
            return WxPayNotifyResponse.success("处理成功");
        } else {
            return WxPayNotifyResponse.fail("处理失败");
        }
    }

    private boolean payNotify(String rechargeNum) {
        try {
            AgentRecharge recharge = this.selectByOrderNum(rechargeNum);
            //不存在订单
            if (recharge == null) {
                return Boolean.FALSE;
            }
            //已处理
            if (recharge.getStatus() >= CommonConstants.NUMBER_TWO) {
                return Boolean.TRUE;
            }
            AgentRecharge up = new AgentRecharge();
            LocalDateTime date = LocalDateTimeUtils.convertDateToLDT(DateUtil.date());
            up.setFinishTime(date);
            up.setStatus(CommonConstants.NUMBER_TWO);
            if (recharge.getPayType().equals(CommonConstants.NUMBER_THREE)) {
                up.setAuditTime(date);
            }
            //更新订单状态
            boolean flag = this.update(up, new UpdateWrapper<AgentRecharge>()
                    .eq("id", recharge.getId()));
            if (!flag) {
                log.info("订单未更新成功 orderId: {} ", rechargeNum);
                return Boolean.FALSE;
            }
            //修改商户余额
            AccountInfo accountInfo = accountInfoService.getById(recharge.getMerchantId());
            AccountInfo upAccount = new AccountInfo();
            upAccount.setId(accountInfo.getId());
            upAccount.setBalance(NumberUtil.add(accountInfo.getBalance(), recharge.getPayAmount()));
            accountInfoService.updateById(upAccount);
            //增加用户余额明细
            platformAccountBalanceRecordService.newBlanceDeail(
                    accountInfo,
                    CommonConstants.NUMBER_ONE, recharge.getRechargeNum(), recharge.getPayAmount());
            //修改用户的订单记录
            PlatformAccountRecharge accountRecharge = platformAccountRechargeService.getByPrepayId(rechargeNum);
            if (accountRecharge == null) {
                log.error("修改用户的订单记录异常：{}", rechargeNum);
                throw new ServiceException("数据订单处理异常");
            }
            PlatformAccountRecharge arUp = new PlatformAccountRecharge();
            arUp.setStatus(CommonConstants.NUMBER_TWO);
            arUp.setId(accountRecharge.getId());
            if (recharge.getPayType().equals(CommonConstants.NUMBER_THREE)) {
                arUp.setAuditTime(date);
            }
            platformAccountRechargeService.updateById(arUp);

            String key2 = "";
            //1支付宝 2 微信 3汇款支付
            switch (recharge.getPayType()) {
                case 1:
                    key2 = key2.concat("支付宝");
                    break;
                case 2:
                    key2 = key2.concat("微信");
                    break;
                case 3:
                    key2 = key2.concat("汇款支付");
                    break;
                default:
                    break;
            }
            //充值通知
            MapBuilder<String, String> content = MapUtil.<String, String>builder()
                    .put("key1", recharge.getPayAmount().toString())
                    .put("key2", key2)
                    .put("key3", LocalDateTimeUtil.format(recharge.getCreateTime(), "yyyy-MM-dd HH:mm:ss"));
            if (recharge.getPayType().equals(CommonConstants.NUMBER_THREE)) {
                JSONObject jsonObject = JSONObject.parseObject(recharge.getPayInfo());
                content.put("key4", jsonObject.getString("name"));
                content.put("key5", jsonObject.getString("account"));
                content.put("key6", DateUtil.parse(jsonObject.getString("paymentTime")).toDateStr());
            }
            agentNoticeService.newNotice(recharge.getAgentInfoId(), AgentNoticeEnum.MSG_0031, null, content.build(), CommonConstants.NUMBER_TWO);
            return Boolean.TRUE;
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceException("订单回调异常");
        }
    }

    /**
     * 获取指定订单号订单
     *
     * @param outTradeNo 订单号
     * @return com.medusa.gruul.platform.api.entity.AgentRecharge
     */
    private AgentRecharge selectByOrderNum(String outTradeNo) {
        return this.getOne(new QueryWrapper<AgentRecharge>().eq("recharge_num", outTradeNo));
    }

    @Override
    public String rechargeNotifyAlipay(Map<String, String> toMap) {
        if (MapUtil.isEmpty(toMap)) {
            log.warn("无效数据");
            return "fail";
        }
        String outTradeNo = toMap.get("out_trade_no");
        if (payNotify(outTradeNo)) {
            return "success";
        }
        return "fail";
    }

    @Override
    public PageUtils<BalanceDetailsVo> balanceDetails(Integer page, Integer size, String startTime, String endTime, String search, String status) {
        Page<AgentRecharge> resultIpage = this.baseMapper.selectPage(new Page<>(page, size),
                new QueryWrapper<AgentRecharge>()
                        .eq("agent_info_id", StpAgentUtil.getLoginId())
                        .eq(StrUtil.isNotEmpty(search), "recharge_num", search)
                        .ge(StrUtil.isNotEmpty(startTime), "create_time", startTime)
                        .le(StrUtil.isNotEmpty(endTime), "create_time", endTime)
                        .eq(status != null, "status", status)
                        .ge(status == null, "status", CommonConstants.NUMBER_ZERO)
                        .orderByDesc("create_time")
        );
        // 封装返回对象
        PageUtils<BalanceDetailsVo> result = new PageUtils<>(null, (int) resultIpage.getTotal(), (int) resultIpage.getSize(), (int) resultIpage.getCurrent());
        // 拷贝部分属性
        if (CollectionUtil.isEmpty(resultIpage.getRecords())) {
            return result;
        }
        List<BalanceDetailsVo> infosVos = resultIpage.getRecords().stream().map(obj -> {
            BalanceDetailsVo balanceDetailsVo = BeanUtil.toBean(obj, BalanceDetailsVo.class);
            AccountInfo accountInfo = accountInfoService.getById(obj.getMerchantId());
            if (accountInfo != null) {
                balanceDetailsVo.setAvatarUrl(accountInfo.getAvatarUrl());
                balanceDetailsVo.setNikeName(accountInfo.getNikeName());
                balanceDetailsVo.setPhone(accountInfo.getPhone());
            }
            return balanceDetailsVo;
        }).collect(Collectors.toList());
        result.setList(infosVos);
        return result;
    }

    @Override
    public PageUtils<BalanceDetailsVo> payOrder(Integer page, Integer size, String startTime, String endTime, Integer payType, Integer status, String phone,
                                                String rechargeNum, String nikeName) {
        List<Long> accountIds = null;
        if (StrUtil.isNotEmpty(phone) || StrUtil.isNotEmpty(nikeName)) {
            List<AccountInfo> infoList = accountInfoService.list(
                    new QueryWrapper<AccountInfo>()
                            .select("id")
                            .like(StrUtil.isNotEmpty(phone), "phone", phone)
                            .like(StrUtil.isNotEmpty(nikeName), "nike_name", nikeName)
            );
            if (CollectionUtil.isNotEmpty(infoList)) {
                accountIds = infoList.stream().map(AccountInfo::getId).collect(Collectors.toList());
            }
        }

        Page<AgentRecharge> resultIpage = this.baseMapper.selectPage(new Page<>(page, size), new QueryWrapper<AgentRecharge>()
                .gt(status == null || status == 0, "status", CommonConstants.NUMBER_ZERO)
                .eq(status != null && status > 0, "status", status)
                .eq(payType != null && payType > 0, "pay_type", payType)
                .ge(StrUtil.isNotEmpty(startTime), "create_time", startTime)
                .le(StrUtil.isNotEmpty(endTime), "create_time", endTime)
                .like(StrUtil.isNotEmpty(rechargeNum), "recharge_num", rechargeNum)
                .in(CollectionUtil.isNotEmpty(accountIds), "merchant_id", accountIds)
                .orderByDesc("create_time")

        );
        // 封装返回对象
        PageUtils<BalanceDetailsVo> result = new PageUtils<>(null, (int) resultIpage.getTotal(), (int) resultIpage.getSize(), (int) resultIpage.getCurrent());
        // 拷贝部分属性
        if (CollectionUtil.isEmpty(resultIpage.getRecords())) {
            return result;
        }
        List<BalanceDetailsVo> infosVos = resultIpage.getRecords().stream().map(obj -> {
            BalanceDetailsVo balanceDetailsVo = BeanUtil.toBean(obj, BalanceDetailsVo.class);
            AccountInfo accountInfo = accountInfoService.getById(obj.getMerchantId());
            if (accountInfo != null) {
                balanceDetailsVo.setAvatarUrl(accountInfo.getAvatarUrl());
                balanceDetailsVo.setNikeName(accountInfo.getNikeName());
                balanceDetailsVo.setPhone(accountInfo.getPhone());
            }
            return balanceDetailsVo;
        }).collect(Collectors.toList());
        result.setList(infosVos);
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void orderOption(OrderOptionDto dto) {
        AgentRecharge agentRecharge = this.getById(dto.getOrderId());
        if (agentRecharge == null) {
            throw new ServiceException("订单不存在");
        }
        if (!CommonConstants.NUMBER_THREE.equals(agentRecharge.getPayType())) {
            throw new ServiceException("非汇款订单无法进行操作");
        }
        if (agentRecharge.getStatus() >= CommonConstants.NUMBER_TWO) {
            throw new ServiceException("该订单已处理");
        }
        //0:生成订单 1:充值中 2:充值成功 3-已关闭
        //收到汇款
        if (dto.getOptionType().equals(CommonConstants.NUMBER_ONE)) {
            //处理成功的订单
            payNotify(agentRecharge.getRechargeNum());
            return;
        }
        //汇款关闭
        if (dto.getOptionType().equals(CommonConstants.NUMBER_TWO)) {
            AgentRecharge up = new AgentRecharge();
            up.setId(agentRecharge.getId());
            up.setStatus(CommonConstants.NUMBER_THREE);
            this.updateById(up);
        }
    }
}
